#include <dspSimulator/instruction/mpy32u.h>


bool MPY32U::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    return checkStandardCondition(dspMachine);
}

void MPY32U::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                               std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    llvm::FunctionType *funcType = llvm::FunctionType::get(irBuilder->getVoidTy(), false);
    // ---------------------------------MPY32U (.unit) src1, src2, dst_o:dst_e---------------------------------------------
    {
        llvm::Function *mpyusM = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "MPY32UKind1",
                                                        module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", mpyusM);
        irBuilder->SetInsertPoint(entryBB);

        auto dstLoRegPtr = getDstLoRegPtr(module, irBuilder);
        auto dstHiRegPtr = getDstHiRegPtr(module, irBuilder);

        auto src2RegPtr = getSrc2LoRegPtr(module, irBuilder);
        auto src2Reg = irBuilder->CreateLoad(src2RegPtr);

        auto src1RegPtr = getSrc1LoRegPtr(module, irBuilder);
        auto src1Reg = irBuilder->CreateLoad(src1RegPtr);

        auto mul1 = irBuilder->CreateZExt(src1Reg, irBuilder->getIntNTy(64));
        auto mul2 = irBuilder->CreateZExt(src2Reg, irBuilder->getIntNTy(64));
        auto res = irBuilder->CreateNSWMul(mul1, mul2);

        auto resLo = irBuilder->CreateTrunc(res, irBuilder->getInt32Ty());
        auto shr = irBuilder->CreateLShr(res, irBuilder->getIntN(64, 32));
        auto resHi = irBuilder->CreateTrunc(shr, irBuilder->getInt32Ty());
        irBuilder->CreateStore(resLo, dstLoRegPtr);
        irBuilder->CreateStore(resHi, dstHiRegPtr);
        irBuilder->CreateRetVoid();
    }
}

std::shared_ptr<Instruction> MPY32U::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<MPY32U> mpy32U;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<10>(data, 2, 0b0110001100) && existStandardCondition(data)) {
            mpy32U = std::make_shared<MPY32U>(dspInstInfo->getAddress(), baseCycle);
            mpy32U->creg_z = extractBits<4>(data, 28);
            mpy32U->dst = extractBits<5>(data, 23).to_ulong();
            mpy32U->src2 = extractBits<5>(data, 18).to_ulong();
            mpy32U->src1 = extractBits<5>(data, 13).to_ulong();
            mpy32U->x = data[12];
            mpy32U->s = data[1];
            mpy32U->p = data[0];
            if (mpy32U->s == 0) {
                mpy32U->funcUnit = FuncUnit::M1;
            } else {
                mpy32U->funcUnit = FuncUnit::M2;
            }
            mpy32U->instFormat = InstFormat::Kind1;
        }
    }
    return mpy32U;
}

void MPY32U::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {

}

std::string MPY32U::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    res += getSrc1Reg32Name() + "," + getSrc2Reg32Name() + "," + getDstReg64Name();
    return res;
}


std::shared_ptr<DspInstruction> MPY32U::clone() {
    return std::make_shared<MPY32U>(*this);
}
